package com.andrewchatham.pony;

import java.io.File;
import java.util.Random;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.MenuItem.OnMenuItemClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;
import android.widget.CompoundButton.OnCheckedChangeListener;

import com.andrewchatham.SynctasticProto.AnnounceResponse;
import com.andrewchatham.SynctasticProto.PlaylistNames;
import com.google.protobuf.InvalidProtocolBufferException;


class PlaylistBinder implements SimpleCursorAdapter.ViewBinder {
  private static final int SYNC_COLUMN = 1;
  private PlaylistDB mDB;

  PlaylistBinder(PlaylistDB db) {
    mDB = db;
  }

  public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
    if (columnIndex == SYNC_COLUMN) {
      CheckBox cb = (CheckBox) view;
      boolean sync = cursor.getInt(SYNC_COLUMN) != 0;
      cb.setChecked(sync);

      // When when the checkbox is toggled, update the database.
      cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
        public void onCheckedChanged(CompoundButton button, boolean isChecked) {
          mDB.setSync(button.getText().toString(), isChecked);
        }
      });
      return true;
    }
    return false;
  }
}

interface ErrorHandler {
  /**
   * Show an error, may be called from any thread
   */
  public void ShowError(final String s);

  /**
   * Display an error from R.strings, may be called from any thread
   * 
   * @param id
   *          an id from R.strings
   */

  public void ShowError(final int id);
}

public class SynctasticPony extends Activity implements
OnSharedPreferenceChangeListener, ErrorHandler {
  private static String TAG = "SynctasticPonyMain";

  private PlaylistDB mPlaylistDB;
  private File mDir;
  private Cursor mPlaylistCursor;
  private WifiManager mWifi;
  // Preferred wireless network.
  private String mSSID;

  public static final int PREFERENCES = Menu.FIRST;
  public static final int REQUEST_PREF = 1;
  private final static int ACTIVITY_SCAN = 1;

  private Server mServer;
  private SyncServiceConnection mSync;
  // Random number specific to this app, used to pair with the server.
  private String mUUID;

  private void setupPlaylistView() {
    mPlaylistCursor = mPlaylistDB.fetchAll();
    startManagingCursor(mPlaylistCursor);
    String[] from = new String[] { PlaylistDB.KEY_NAME, PlaylistDB.KEY_SYNC };
    int[] to = new int[] { R.id.syncbutton, R.id.syncbutton };
    SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
        R.layout.playlistrow, mPlaylistCursor, from, to);
    ListView view = (ListView) findViewById(R.id.playlists);
    adapter.setViewBinder(new PlaylistBinder(mPlaylistDB));
    view.setAdapter(adapter);
  }

  private void RefreshPlaylists() {
    Log.d(TAG, "Refresh playlists " + mServer.formatRequest("/playlist", null));
    new AsyncHTTPRequest(mServer.formatRequest("/playlists", null),
        new AsyncHTTPRequest.Handler() {
      public void HandleResponse(boolean success, byte[] content) {
        PlaylistNames playlists;
        try {
          playlists = PlaylistNames.parseFrom(content);
        } catch (InvalidProtocolBufferException e) {
          Log.d(TAG, e.toString());
          return;
        }
        mPlaylistDB.setPlaylists(playlists);
        runOnUiThread(new Runnable() {
          public void run() {
            mPlaylistCursor.requery();
          }
        });
      }
    });
  }

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    mWifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    SharedPreferences prefs = PreferenceManager
    .getDefaultSharedPreferences(this);
    ConnectivityManager connectivity = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    mServer = new Server(this, this, connectivity
        .getNetworkInfo(ConnectivityManager.TYPE_WIFI), prefs);

    PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
    setPreferences(prefs);
    prefs.registerOnSharedPreferenceChangeListener(this);
    maybeCreateUUID(prefs);
    
    mSync = new SyncServiceConnection();
    bindService(new Intent(this, SyncService.class),
        mSync, Context.BIND_AUTO_CREATE);

    mPlaylistDB = new PlaylistDB(this);
    mPlaylistDB.open();

    if (!Environment.getExternalStorageState()
        .equals(Environment.MEDIA_MOUNTED)) {
      Log.e(TAG, "Not mounted properly "
          + Environment.getExternalStorageState());
      ShowError(R.string.not_mounted);
      return;
    }
    mDir = new File(Environment.getExternalStorageDirectory(), "SynctasticPony");
    mDir.mkdirs();

    setupPlaylistView();
    Button sync = (Button) findViewById(R.id.sync);
    sync.setOnClickListener(new View.OnClickListener() {
      public void onClick(View v) {
        if (!isConnectedToNetwork()) {
          ShowError(R.string.must_connect);
          return;
        }
        mSync.startSync();
      }
    });

    Button cancel = (Button) findViewById(R.id.cancel);
    cancel.setOnClickListener(new View.OnClickListener() {
      public void onClick(View v) {
        ShowError(R.string.cancel_message);
        mSync.stopSync();
      }
    });

    Button refresh = (Button) findViewById(R.id.refresh);
    refresh.setOnClickListener(new View.OnClickListener() {
      public void onClick(View v) {
        if (!isConnectedToNetwork()) {
          ShowError(R.string.must_connect);
          return;
        }
        if (!mServer.checkPairing()) {
          showPairingInstructions();
          return;
        }
        RefreshPlaylists();
      }
    });
  }

  private void showPairingInstructions() {
    AlertDialog.Builder builder = new AlertDialog.Builder(SynctasticPony.this);
    builder.setTitle(getString(R.string.pairing_title));
    builder.setMessage(String
        .format(getString(R.string.pairing_message), mUUID));
    builder.setCancelable(true);
    builder.setPositiveButton(android.R.string.ok, null);
    builder.show();
  }

  /**
   * Return whether we're connected to the preferred wireless network. Used as a
   * test before network actions.
   * 
   * @return
   */
  private boolean isConnectedToNetwork() {
    if (!mWifi.isWifiEnabled())
      return false;
    return mSSID != null && mSSID.equals(mWifi.getConnectionInfo().getSSID());
  }

  public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);

    MenuItem settings = menu.add(getString(R.string.settings));
    settings.setIcon(android.R.drawable.ic_menu_preferences);
    settings.setIntent(new Intent(this, Preferences.class));

    MenuItem scan = menu.add(R.string.scan);
    scan.setIcon(android.R.drawable.ic_menu_manage);
    scan.setIntent(new Intent(this, Scan.class));
    scan.setOnMenuItemClickListener(new OnMenuItemClickListener() {
      public boolean onMenuItemClick(MenuItem item) {
        startActivityForResult(item.getIntent(), ACTIVITY_SCAN);
        return true;
      }
    });

    MenuItem help = menu.add(R.string.help_menu_text);
    help.setIcon(android.R.drawable.ic_menu_help);
    help.setOnMenuItemClickListener(new OnMenuItemClickListener() {
      public boolean onMenuItemClick(MenuItem item) {
        new AlertDialog.Builder(SynctasticPony.this)
        .setTitle(R.string.app_name).setPositiveButton(R.string.help_ok,
            null).setMessage(R.string.help_message).show();
        return true;
      }

    });
    return true;
  }

  /**
   * Show an error, may be called from any thread
   */
  public void ShowError(final String s) {
    runOnUiThread(new Runnable() {
      public void run() {
        ShowErrorInternal(s);
      }
    });
  }

  /**
   * Show an error, may be called from any thread
   */
  public void ShowError(final int id) {
    runOnUiThread(new Runnable() {
      public void run() {
        ShowErrorInternal(id);
      }
    });
  }

  private void ShowErrorInternal(int id) {
    ShowError(getString(id));
  }

  private void ShowErrorInternal(String s) {
    Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
  }

  /**
   * Callback when user alters preferences.
   */
  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    setPreferences(prefs);
  }

  /**
   * If the uuid preference isn't set, create a new random UUID and store it.
   * 
   * @param prefs
   */
  public void maybeCreateUUID(SharedPreferences prefs) {
    SharedPreferences.Editor editor = prefs.edit();
    String uuid = prefs.getString(getString(R.string.uuid_key), null);
    if (uuid != null)
      return;
    uuid = String.format("%x", new Random().nextInt(1 << 25));
    Log.d(TAG, String.format("Creating uuid=%s", uuid));
    editor.putString(getString(R.string.uuid_key), uuid);
    editor.commit();
  }

  /**
   * Set the state of the application based on prefs. This should be called
   * after every preference change or when starting up.
   * 
   * @param prefs
   */
  private void setPreferences(SharedPreferences prefs) {
    mUUID = prefs.getString(getString(R.string.uuid_key), null);
    mSSID = prefs.getString(getString(R.string.ssid_key), null);
  }

  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == ACTIVITY_SCAN && resultCode != RESULT_CANCELED) {
      AnnounceResponse server;
      try {
        server = AnnounceResponse.parseFrom(data
            .getByteArrayExtra(Scan.RESPONSE_KEY));
      } catch (InvalidProtocolBufferException e) {
        e.printStackTrace();
        return;
      }
      mServer.setHostPort(server.getAddress(), server.getPort());
      String name = server.getHostname();
      Log.d(TAG, "Prefix now " + mServer.formatRequest("/blah", null));

      // Change the preferences to indicate that we should autodiscover the
      // server with this name next time.
      SharedPreferences prefs = PreferenceManager
      .getDefaultSharedPreferences(this);
      SharedPreferences.Editor editor = prefs.edit();
      editor.putString(getString(R.string.discovered_name_key), name);
      editor.putString(getString(R.string.host_key), "");
      // Remember the wifi network we're on.
      editor.putString(getString(R.string.ssid_key), mWifi.getConnectionInfo()
          .getSSID());
      editor.commit();

      if (!mServer.checkPairing())
        showPairingInstructions();
    }
  }
}